package com.ymx.darling.bean.entity;

import com.ymx.darling.bean.entity.dynamic.DynamicSchemaProcessor;
import com.ymx.darling.bean.entity.dynamic.DynamicTableProcessor;
import com.ymx.darling.bean.entity.fill.EntityFillProcessor;
import com.ymx.darling.bean.entity.fill.FillEntity;
import com.ymx.darling.exception.EntityException;
import com.ymx.darling.exception.ExceptionFactory;
import com.ymx.darling.exception.ExceptionWrapper;
import com.ymx.darling.executor.mapping.relation.RelationFieldInfo;
import com.ymx.darling.logicdelete.LogicDeleteManager;
import com.ymx.darling.tenant.TenantManager;
import com.ymx.darling.util.ReflectUtil;
import com.ymx.darling.util.StringUtil;
import com.ymx.darling.wrapper.UpdateWrapper;
import com.ymx.darling.wrapper.Wrapper;
import com.ymx.darling.bean.entity.id.IdType;
import com.ymx.darling.bean.entity.id.IdWorker;

import java.lang.reflect.Field;
import java.util.Objects;

/**
 * BeanInfo的管理器 负责BeanInfo的注册 以及根据表名返回对应的BeanInfo实例
 * 您还可以在框架启动前调用此类中的相关方法配置一些处理器
 * @author 爱java的小于
 */
public class BeanInfoManager {
    /**
     * id生成器
     */
    private final static IdWorker idWorker = new IdWorker(31L,31L);
    /**
     * BeanInfoRepository
     */
    private static BeanInfoRepository beanInfoRepository;
    /**
     * 实体填充处理器
     */
    private static EntityFillProcessor entityFillProcessor;
    /**
     * 动态schema处理器
     */
    private static DynamicSchemaProcessor dynamicSchemaProcessor;

    /**
     * 动态表名处理器
     */
    private static DynamicTableProcessor dynamicTableProcessor;


    public static void registerBeanInfoRepository(BeanInfoRepository repository) {
        beanInfoRepository = repository;
    }

    private static BeanInfoRepository getBeanInfoRepository() {
        if (beanInfoRepository == null) {
            throw new NullPointerException("beanInfoRepository未初始化加载,请重新设置BeanInfoRepository.");
        }
        return beanInfoRepository;
    }

    /**
     * 判断指定表是否已经注册
     * @param tableName 表名
     * @return boolean
     */
    public static boolean hasTable(String tableName) {
        tableName = TableAlias.splitTableAndAlias(tableName).getTableName();
        return getBeanInfoRepository().hasTable(tableName);
    }


    /**
     * 根据表的名称返回实体信息包装类
     * 调用此方法的前提必须确保实体已经调用BeanInfoUtil.registerEntity注册过 否则返回null
     * @param tableName 表名
     * @return BeanInfo
     */
    public static BeanInfo getTableBeanInfo(String tableName) {
        TableAlias tableAlias = TableAlias.splitTableAndAlias(tableName);
        return getBeanInfoRepository().getTableBeanInfo(tableAlias.getTableName());
    }


    /**
     * 返回已注册实体的BeanInfo实例
     * 如果没有注册抛出异常
     * @param entity class
     * @return beanInfo
     */
    public static BeanInfo getBeanInfo(Class<?> entity) {
        BeanInfo beanInfo = getBeanInfoRepository().getBeanInfo(entity);
        if (beanInfo == null) {
            throw new EntityException("实体'"+entity.getName()+"'未注册");
        }
        return beanInfo;
    }

    /**
     * 判断指定类型的实体是否已经注册过
     * @param type type
     * @return boolean
     */
    public static boolean isRegister(Class<?> type) {
        return getBeanInfoRepository().getBeanInfo(type) != null;
    }


    /**
     * 判断指定实体的类型是否已经注册
     * @param entity 实体
     * @return boolean
     */
    public static boolean isRegister(Object entity) {
        return entity != null && isRegister(entity.getClass());
    }


    /**
     * 获取当前的表名 如果是动态表名则返回动态表名
     * @param tableName 表名
     * @return String
     */
    public static String getCurrentTableName(String tableName) {
        TableAlias tableAlias = TableAlias.splitTableAndAlias(tableName);
        // 判断表名是否已经注册
        if (beanInfoRepository.hasTable(tableAlias.getTableName())) {
            BeanInfo tableBeanInfo = beanInfoRepository.getTableBeanInfo(tableAlias.getTableName());
            String schemaName = getSchemaName(tableBeanInfo, tableAlias);
            StringBuilder tableBuilder = new StringBuilder();
            // 判断包含schema
            if (StringUtil.isNoNull(schemaName)) {
                if (dynamicSchemaProcessor != null) {
                    tableBuilder.append(dynamicSchemaProcessor.process(schemaName));
                } else {
                    tableBuilder.append(schemaName);
                }
                tableBuilder.append(".");
            }
            // 判断注册动态表名处理器
            String currentTableName = tableAlias.getTableName();
            if (dynamicTableProcessor != null) {
                currentTableName = dynamicTableProcessor.process(currentTableName);
            }
            // 判断是否包含别名
            if (tableAlias.hasAlias()) {
                currentTableName = currentTableName + " AS " + tableAlias.getAliasName();
            }
            return tableBuilder.append(currentTableName).toString();
        }
        return tableName;
    }


    /**
     * 获取schemaName 优先返回tableAlias中的schemaName
     * @param tableBeanInfo tableBeanInfo
     * @param tableAlias tableAlias
     * @return String
     */
    private static String getSchemaName(BeanInfo tableBeanInfo, TableAlias tableAlias) {
        if (StringUtil.isNoNull(tableAlias.getSchemaName())) {
            return tableAlias.getSchemaName();
        }
        return tableBeanInfo.getSchemaName();
    }


    /**
     * 注册动态schema处理器
     * @param processor processor
     */
    public static void registerDynamicSchemaProcessor(DynamicSchemaProcessor processor) {
        dynamicSchemaProcessor  = processor;
    }


    /**
     * 注册动态表名处理器
     * @param processor processor
     */
    public static void registerDynamicTableProcessor(DynamicTableProcessor processor) {
        dynamicTableProcessor = processor;
    }


    /**
     * 注册实体填充处理器
     * @param processor processor
     */
    public static void registerEntityFillProcessor(EntityFillProcessor processor) {
        entityFillProcessor = processor;
    }


    /**
     * 是否开启填充功能
     * @param beanInfo beanInfo
     */
    public static boolean isEnableFill(BeanInfo beanInfo) {
        return beanInfo.isFillable() && Objects.nonNull(entityFillProcessor);
    }


    /**
     * 执行插入填充
     * @param obj obj
     * @param beanInfo beanInfo
     */
    public static void doInsertEntityFill(Object obj, BeanInfo beanInfo) {
        if (isEnableFill(beanInfo)) {
            try {
                entityFillProcessor.fillBeforeInsert(new FillEntity(obj, beanInfo));
            } catch (Exception exception) {
                throw ExceptionFactory.fillEntityException(beanInfo.getModule(), "执行插入填充时抛出异常", exception);
            }
        }
    }


    /**
     * 执行更新填充
     * @param obj obj
     * @param beanInfo beanInfo
     */
    public static void doUpdateEntityFill(Object obj, BeanInfo beanInfo) {
        if (isEnableFill(beanInfo)) {
            try {
                entityFillProcessor.fillBeforeUpdate(new FillEntity(obj, beanInfo));
            } catch (Exception exception) {
                throw ExceptionFactory.fillEntityException(beanInfo.getModule(), "执行更新填充时抛出异常", exception);
            }
        }
    }


    /**
     * 判断实体类中是否存在主键
     * @param beanInfo beanInfo
     * @return boolean
     */
    public static boolean isPrimaryKey(BeanInfo beanInfo) {
        return Objects.nonNull(beanInfo) &&
                Objects.nonNull(beanInfo.getIdColumnInfo()) &&
                beanInfo.getIdColumnInfo().getIdType() == IdType.ASSIGN;
    }


    /**
     * 为实体id字段生成分布式主键
     * @param obj 实体
     * @param beanInfo 实体反射信息的封装类
     */
    public static void doPrimaryKeyFill(Object obj, BeanInfo beanInfo) {
        Field idField = beanInfo.getIdColumnInfo().getField();
        try {
            if (idField.getType() == String.class) {
                ReflectUtil.setValue(idField, obj, String.valueOf(idWorker.nextId()));
            } else {
                ReflectUtil.setValue(idField, obj, idWorker.nextId());
            }
        } catch (ExceptionWrapper exceptionWrapper) {
            throw ExceptionFactory.wrapException("id字段'%s'填充数据时抛出异常", exceptionWrapper);
        }
    }


    /**
     * 添加逻辑删除和租户条件
     * @param tableName 表名
     * @param wrapper wrapper
     */
    public static void addLogicDeleteAndTenantId(String tableName, Wrapper<?> wrapper) {
        if (hasTable(tableName)) {
            BeanInfo tableBeanInfo = BeanInfoManager.getTableBeanInfo(tableName);
            if (LogicDeleteManager.isLogicDeleteEnabled(tableBeanInfo)) {
                LogicDeleteManager.addConditionByWrapper(tableBeanInfo.getLogicDeleteColumn(), tableName, wrapper);
            }
            if (TenantManager.isTenantEnabled(tableBeanInfo)) {
                TenantManager.addConditionByWrapper(tableBeanInfo.getTenantIdColumn(), tableName, wrapper);
            }
        }
    }


    /**
     * 判断添加乐观锁版本号
     * @param entity 更新的实体类型
     * @param updateWrapper updateWrapper
     */
    public static void addVersionCondition(Object entity, UpdateWrapper updateWrapper) {
        BeanInfo beanInfo = getBeanInfo(entity.getClass());
        FieldInfo versionColumnInfo = beanInfo.getVersionColumnInfo();
        if (versionColumnInfo != null) {
            String versionColumn = versionColumnInfo.getColumn();
            Object versionValue = ReflectUtil.getValue(versionColumnInfo.getField(), entity);
            updateWrapper.setSegment("${arg0} = ${arg0} + 1", versionColumn);
            updateWrapper.eq(versionColumn, versionValue);
        }
    }


    /**
     * 获取类中的关系字段信息
     * @param entityClass 必须是实体类
     * @param fieldName 关联字段的名字
     * @return RelationFieldInfo
     */
    public static RelationFieldInfo getRelationFieldInfo(Class<?> entityClass, String fieldName) {
        BeanInfo beanInfo = getBeanInfo(entityClass);
        for (RelationFieldInfo relationFieldInfo : beanInfo.getRelationFieldInfoList()) {
            if (relationFieldInfo.getFieldName().equals(fieldName)) {
                return relationFieldInfo;
            }
        }
        return null;
    }
}
